#include "YUVTexture.h"

struct YUVTexturePrivate
{
	QRhiTexture::Format mFormat;
	QSize				mSize;
	QByteArray			mData;

	std::shared_ptr<QRhiTexture> mRhiTexturePtr;
};
YUVTexture::YUVTexture()
	: d(new YUVTexturePrivate)
{
	setFiltering(Linear);
}

YUVTexture::~YUVTexture()
{
	delete d;
}

qint64 YUVTexture::comparisonKey() const
{
	if (d->mRhiTexturePtr)
	{
		return qint64(qintptr(d->mRhiTexturePtr.get()));
	}
	return qint64(qintptr(this));
}

QRhiTexture* YUVTexture::rhiTexture() const
{
	return d->mRhiTexturePtr.get();
}

QSize YUVTexture::textureSize() const
{
	return d->mSize;
}

bool YUVTexture::hasAlphaChannel() const
{
	return d->mFormat == QRhiTexture::RGBA8 || QRhiTexture::BGRA8;
}

bool YUVTexture::hasMipmaps() const
{
	return mipmapFiltering() != QSGTexture::None;
}

void YUVTexture::commitTextureOperations(QRhi* rhi, QRhiResourceUpdateBatch* resourceUpdates)
{
	bool needsRebuild = d->mRhiTexturePtr && d->mRhiTexturePtr->pixelSize() != d->mSize;
	if (!d->mRhiTexturePtr)
	{
		QRhiTexture::Flags flags;
		if (hasMipmaps())
		{
			flags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
		}
		d->mRhiTexturePtr.reset(rhi->newTexture(d->mFormat, d->mSize, 1, flags));
		needsRebuild = true;
	}
	if (needsRebuild)
	{
		d->mRhiTexturePtr->setPixelSize(d->mSize);
		bool created = d->mRhiTexturePtr->create();
		if (!created)
		{
			qWarning("Failed to build texture (size %dx%d)", d->mSize.width(), d->mSize.height());
			return;
		}
	}
	if (!d->mData.isEmpty())
	{
		QRhiTextureSubresourceUploadDescription subresDesc(d->mData.constData(), d->mData.size());
		subresDesc.setSourceSize(d->mSize);
		subresDesc.setDestinationTopLeft(QPoint(0, 0));
		QRhiTextureUploadEntry		 entry(0, 0, subresDesc);
		QRhiTextureUploadDescription desc(entry);
		resourceUpdates->uploadTexture(d->mRhiTexturePtr.get(), desc);
		d->mData.clear();
	}
}

void YUVTexture::setRhiTexture(QRhiTexture* rhiTexture)
{
	d->mRhiTexturePtr.reset(rhiTexture);
}

void YUVTexture::setData(QRhiTexture::Format f, const QSize& s, const QByteArray& data)
{
	d->mFormat = f;
	d->mData   = data;
	d->mSize   = s;
}
